package com.hup.minicontroller.common.framework;

import android.util.Log;

import java.util.Objects;

import lombok.Setter;

/**
 * 处理stack打印的Logger,打印时过滤不是本项目包下的stack,简化/突出stack关键信息
 * 混淆注意,混淆时如果业务类被抽取到packagePrefix外,则会丢失stack日志
 *
 * @author hugan
 * @date 2022/4/16
 */
public abstract class AppLogger {
    private static final String TAG = AppLogger.class.getSimpleName();

    /**
     * 用于过滤stack的包名前缀,省缺是2层包名"com.xxx"
     */
    private static String packagePrefix;
    /**
     * 最大stack打印行数,避免死循环或递归等造成的过量日志
     */
    @Setter
    private static int maxStackCount = 20;
    /**
     * 打印stack时,最多打印n层cause,
     * ..数据库/SpringBoot启动异常:getMessage时,都会把case的message合并在一起的,
     */
    @Setter
    private static int maxCauseCount = 3;

    static {
        packagePrefix = Objects.requireNonNull(AppLogger.class.getPackage()).getName();
        int dotCount = 0;
        int endIndex = packagePrefix.length();
        for (int i = 0; i < packagePrefix.length(); i++) {
            if (packagePrefix.charAt(i) != '.') continue;
            dotCount++;
            if (dotCount == 2) {
                //找到2个'.'
                endIndex = i;
                break;
            }
        }
        packagePrefix = packagePrefix.substring(0, endIndex);
    }

    /**
     * {@link #packagePrefix}
     *
     * @param appPackage main入口类的包对象,一般只有三层"com.xxx.project"
     */
    public static void initPackage(Package appPackage) {
        packagePrefix = appPackage.getName();
        //log.info("packagePrefix={}", packagePrefix);
    }

    /**
     * 相当于公用的logger,可以用于(反逆向时)隐藏原日志输出的业务类
     */
    public static void error(String msg) {
        Log.e(TAG, msg);
    }

    public static void error(String msg, Throwable t) {
        Log.e(TAG, getStack(msg, t));
    }

    public static String getStack(Throwable e) {
        return getStack(null, e);
    }

    /**
     * @return 无msgHead时, 以'\n'开头的stack内容;有msgHead时,msgHead是第一行
     */
    public static String getStack(String msgHead, Throwable e) {
        StringBuilder sb = new StringBuilder();
        if (msgHead != null) sb.append(msgHead);

        int addedStackCount = 0;
        int causeCount = 1;
        Throwable cause = e;
        while (cause != null) {
            //如 org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'GET' not supported
            sb.append('\n').append(cause.getClass()).append(": ");
            String message = cause.getMessage();
            if (message != null) sb.append(message);

            StackTraceElement[] stack = cause.getStackTrace();
            if (stack.length == 0) {
                //1.这个线程没有被开启；2.这个线程被开启了但是没有被系统运行过（因为线程运行是需要根据一定规律轮换的）；3.这个线程结束了。
                Log.e(TAG, "StackTrace长度为0", cause);
                break;
            }
            //总是打印第一行,异常出处
            sb.append("\n  \tat ").append(stack[0]);
            addedStackCount++;
            if (addedStackCount > maxStackCount) break;

            //过滤和收集该cause的stack
            for (int i = 1; i < stack.length; i++) {
                StackTraceElement element = stack[i];
                if (element.getClassName().startsWith(packagePrefix)) {
                    sb.append("\n  \tat ").append(element);
                    addedStackCount++;
                    if (addedStackCount > maxStackCount) break;
                }
            }

            //如果没break,则继续抓取case
            cause = cause.getCause();
            causeCount++;
            if (causeCount > maxCauseCount) break;
        }
        return sb.toString();
    }

}
